package Metadata;

import java.io.IOException;

import Common.*;
import Common.Types;
import exceptions.*;
import heap.*;
import Transaction.*;
import java.util.*;

public class AttrMgr extends Heapfile implements GlobalConst {

	public AttrMgr(String filename, Transaction tx) throws Exception {
		super(filename, tx);
		// System.out.println("haha");
		int sizeOfInt = 4;
		int sizeOfFloat = 4;
		tuple = new Tuple(Tuple.maxSize);
		attrs = new Types[14];
		// rel name
		attrs[0] = new Types(Types.CHAR);
		// attr name
		attrs[1] = new Types(Types.CHAR);
		// attr offset
		attrs[2] = new Types(Types.INT);
		// attr pos
		attrs[3] = new Types(Types.INT);
		// Types will be represented by an integer
		// 0 = string, 1 = real, 2 = integer
		attrs[4] = new Types(Types.INT);
		// attr len
		attrs[5] = new Types(Types.INT);
		// Index
		attrs[6] = new Types(Types.INT);
		// min val
		attrs[7] = new Types(Types.CHAR); // ????? BK ?????
		// max val
		attrs[8] = new Types(Types.CHAR); // ????? BK ?????
		// Pk
		attrs[9] = new Types(Types.INT);
		// Fk
		attrs[10] = new Types(Types.INT);
		// Fk Nember del a relation
		attrs[11] = new Types(Types.CHAR);
		// Fk Nember del atributo
		attrs[12] = new Types(Types.CHAR);
		// Check
		attrs[13] = new Types(Types.CHAR);

		// Find the largest possible tuple for values attrs[7] & attrs[8]
		max = 10;
		if (sizeOfInt > max)
			max = (short) sizeOfInt;
		if (sizeOfFloat > max)
			max = (short) sizeOfFloat;

		strSizes = new short[4];
		strSizes[0] = (short) MAXNAME;
		strSizes[1] = (short) MAXNAME;
		strSizes[2] = max;
		strSizes[3] = max;

		// tuple.setHdr((short) 9, attrs, strSizes);
		creatTuple();
		// tuple = new Tuple(Tuple.maxSize);
	}

	private void creatTuple() throws Exception, InvalidTypeException,
			IOException {
		tuple = new Tuple(Tuple.maxSize);
		initTuple();
	}

	private void initTuple() throws Exception {
		tuple.setHdr((short) 10, attrs, strSizes);

	}

	/**
	 * Get attribute information
	 * 
	 * @param relname
	 * @param attrName
	 * @param record
	 */
	public attrSchema getInfo(String relname, String attrName, Transaction tx)
			throws Exception {
		RID rid = null;
		Scan pscan = null;
		pscan = new Scan(this, tx);

		rid = new RID();
		attrSchema record = new attrSchema();
		//System.out.println("attrName:"+attrName+" "+"tableName:"+relname);
		while (true) {
			tuple = pscan.getNext(rid, tx, fileName);
			if (tuple == null) {

				pscan.closeScan();
				throw new DBCatalogNotFoundException(null,
						"Catalog: attrbute not found");
			}
			initTuple();
			readTuple(tuple, record, tx);
			if (record.relName.equalsIgnoreCase(relname)
					&& record.attrName.equalsIgnoreCase(attrName)) {
				pscan.closeScan();
				
				return record;
			}
		}
	}

	/**
	 * GET ALL ATTRIBUTES OF A RELATION
	 * 
	 * @param relation
	 * @param attrCnt
	 * @param Attrs
	 * @return attrCount
	 * @throws Exception
	 */
	public attrSchema[] getTableInfo(String relation, Transaction tx)
			throws Exception {
		tableSchema record = null;
		attrSchema attrRec = null;

		RID rid = null;
		Scan pscan = null;
		int count = 0;

		record = new tableSchema();
		rid = new RID();

		int attrCnt = 0;

		GoggleDB.tableMgr.getInfo(relation, record, tx);

		// SET ATTRIBUTE COUNT BY REFERENCE
		attrCnt = record.attrCnt;

		attrSchema[] Attrs = new attrSchema[attrCnt];
		if (attrCnt == 0)
			return Attrs;

		// OPEN SCAN
		pscan = new Scan(this, tx);

		// SCAN FILE
		while (true) {

			tuple = pscan.getNext(rid, tx, fileName);
			if (tuple == null) {
				pscan.closeScan();
				throw new DBCatalogNotFoundException(null,
						"Catalog: Table not Found!");
			}
			initTuple();
			attrRec = new attrSchema();
			readTuple(tuple, attrRec, tx);

			if (attrRec.relName.equalsIgnoreCase(relation) == true) {
				Attrs[attrRec.attrPos - 1] = attrRec;
				count++;
			}

			if (count == attrCnt) // if all atts found
				break;
		}

		pscan.closeScan();
		// System.out.println(Attrs[0].attrName);
		return Attrs;
	}

	public int getRelInfoEm(String relation, attrSchema[][] Attrs,
			Transaction tx) throws Exception {
		int attrCnt = 0;
		tableSchema record = null;
		attrSchema attrRec = null;
		int status;
		int recSize;
		RID rid = null;
		Scan pscan = null;
		int count = 0;

		record = new tableSchema();
		rid = new RID();

		GoggleDB.tableMgr.getInfo(relation, record, tx);

		// SET ATTRIBUTE COUNT
		attrCnt = record.attrCnt;
		if (attrCnt == 0)
			return attrCnt;

		// OPEN SCAN

		pscan = new Scan(this, tx);

		// ALLOCATE ARRAY
		Attrs[0] = new attrSchema[attrCnt];
		// SCAN FILE

		while (true) {

			tuple = pscan.getNext(rid, tx, fileName);
			if (tuple == null) {
				pscan.closeScan();
				throw new DBException(null, "Catalog: Index not Found!");
			}
			initTuple();
			attrRec = new attrSchema();
			readTuple(tuple, attrRec, tx);

			if (attrRec.relName.equalsIgnoreCase(relation) == true) {
				Attrs[0][attrRec.attrPos - 1] = attrRec;
				count++;
			}

			if (count == attrCnt) // if all atts found
				break;
		}

		pscan.closeScan();

		return attrCnt;
	}

	public int getTupleStructure(String tableName, int attrCnt,
			Types[] typeArray, short[] sizeArray, Transaction tx)
			throws Exception {
		attrSchema[] attrs = null;
		int i, x;

		// GET ALL OF THE ATTRIBUTES
		attrs = getTableInfo(tableName, tx);
		//attrs = Global.TableMgr.relInfo.get(tableName);
		attrCnt = attrs.length;
		// FILL ARRAYS WITH TYPE AND SIZE DATA
		for (x = 0, i = 0; i < attrCnt; i++) {
			// System.out.println("typea:"+typeArray[i]+" "+attrs[i]);
			typeArray[i].attrType = attrs[i].attrType.attrType;
			int type = attrs[i].attrType.attrType;
			if (type == Types.CHAR || type == Types.DATETIME || type == Types.VARCHAR
					|| type == Types.DECIMAL ||type==Types.TIMESTAMP) {
				// System.out.println("attrlen:"+attrs[i].attrLen+" "+i+"
				// "+attrs[i].attrName);
				sizeArray[x] = (short) attrs[i].attrLen;
				x++;
			}
		}

		return attrCnt;
	}

	public int getTupleStructureEm(String relation, Types[][] typeArray,
			short[][] sizeArray, Transaction tx) throws Exception {
		int attrCnt;
		int status;
		int stringcount = 0;
		attrSchema[][] attrs_parche = new attrSchema[1][];
		attrSchema[] attrs = null;
		int i, x;

		// GET ALL OF THE ATTRIBUTES

		attrCnt = getRelInfoEm(relation, attrs_parche, tx);
		attrs = attrs_parche[0];

		// ALLOCATE TYPEARRAY

		typeArray[0] = new Types[attrCnt];
		sizeArray[0] = new short[attrCnt];
		// LOCATE STRINGS
		for (i = 0; i < attrCnt; i++) {
			if (attrs[i].attrType.attrType == Types.CHAR)
				stringcount++;
		}

		// FILL ARRAYS WITH TYPE AND SIZE DATA

		for (x = 0, i = 0; i < attrCnt; i++) {
			typeArray[0][i] = new Types(attrs[i].attrType.attrType);
			int type = attrs[i].attrType.attrType;
			if (type == Types.CHAR || type == Types.DATETIME|| type == Types.VARCHAR
					|| type == Types.DECIMAL ||type==Types.TIMESTAMP) {
				sizeArray[0][x] = (short) attrs[i].attrLen;
				x++;
			}
		}

		return attrCnt;
	};

	// ADD ATTRIBUTE ENTRY TO CATALOG
	public void addInfo(attrSchema record, Transaction tx) throws Exception,
			HFBufMgrException, DBInvalidSlotNumberException {
		creatTuple();
		makeTuple(tuple, record);
		insertRecord(tuple.getTupleByteArray(tx), tx, fileName);

	}

	public void removeInfo(String relation, String attrName, Transaction tx)
			throws Exception {
		RID rid = new RID();
		Scan pscan = null;
		attrSchema record = new attrSchema();

		if ((relation == null) || (attrName == null))
			throw new DBException(null, "MISSING_PARAM");

		// OPEN SCAN
		try {
			pscan = new Scan(this, tx);
		} catch (Exception e1) {
			throw new DBException(e1, "scan failed");
		}
		

		// SCAN FILE
		while (true) {
			
				tuple = pscan.getNext(rid, tx, fileName);
				//System.out.println(tuple);
				if (tuple == null)
					throw new DBException(null, "Catalog: Attribute not Found!");
				initTuple();
				readTuple(tuple, record, tx);
			

			if (record.relName.equalsIgnoreCase(relation) == true
					&& record.attrName.equalsIgnoreCase(attrName) == true) {
				try {
					deleteRecord(rid, tx, fileName);
				} catch (Exception e3) {
					throw new DBException(e3, "deleteRecord failed");
				}
				return;
			}
		}
	}

	public void makeTuple(Tuple tuple, attrSchema record) throws IOException {
		try {
			tuple.SsetStrFld(1, record.relName);
			tuple.SsetStrFld(2, record.attrName);
			tuple.SsetIntFld(3, record.attrOffset);
			tuple.SsetIntFld(4, record.attrPos);

			tuple.SsetIntFld(5, record.attrType.attrType);

			tuple.SsetIntFld(6, record.attrLen);
			tuple.SsetIntFld(7, record.indexCnt);
			tuple.SsetIntFld(10, record.pk);
		} catch (Exception e1) {
		}
	};

	public void readTuple(Tuple tuple, attrSchema record, Transaction tx)
			throws Exception {
		try {
			
			record.relName = tuple.getStrFld(1, tx, fileName);
			record.attrName = tuple.getStrFld(2, tx, fileName);
			record.attrOffset = tuple.getIntFld(3, tx, fileName);
			record.attrPos = tuple.getIntFld(4, tx, fileName);

			int temp;
			temp = tuple.getIntFld(5, tx, fileName);

			record.attrType = new Types(temp);

			record.attrLen = tuple.getIntFld(6, tx, fileName);
			record.indexCnt = tuple.getIntFld(7, tx, fileName);
			record.pk = tuple.getIntFld(10, tx, fileName);
		} catch (Exception e1) {
			e1.printStackTrace();
		}

	}

	Tuple tuple;

	short[] strSizes;

	Types[] attrs;

	short max;
	

}
